home *** CD-ROM | disk | FTP | other *** search
- /*
- * K e r m i t File Transfer Utility
- *
- * UNIX Kermit, Columbia University, 1981, 1982, 1983
- * Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell
- *
- * Also: Jim Guyton, Rand Corporation
- * Walter Underwood, Ford Aerospace
- * David Ragozin, Univ. Washinton
- * John Sambrook, Univ. Washington, Bioengineering.
- *
- * usage: kermit c [lbe line baud escapechar] to connect
- * kermit s [d..iflb line baud] file ... to send files
- * kermit r [d..iflb line baud] to receive files
- *
- * where c=connect, s=send, r=receive,
- * d=debug, i=image mode, f=no filename conversion, l=tty line,
- * b=baud rate, e=escape char.
- *
- * If UCB4X then
- *
- * After first c, s, or r enters command mode with 'Kermit: ' prompt
- *
- * Commands: c[lbe line baud esc.char] to (re)connect
- * s[diflb line baud] file ... to send file(s)
- * r[diflb line baud] to receive
- * ![cmd] for shell escape
- * q to quit
- * ? or h to print this list
- * NOTE:
- * In command mode l(ine) or b(aud) parameters for c,s or r
- * need not be specified if unchanged from previous command(s).
- *
- * endif UCB4X may work on others but tested only on 4.1 and 4.1c
- */
-
- /*
- * Modification History:
- * Oct. 17 Included fixes from Alan Crosswell (CUCCA) for IBM_UTS:
- * - Changed MYEOL character from \n to \r.
- * - Change char to int in bufill so getc would return -1 on
- * EOF instead of 255 (-1 truncated to 8 bits)
- * - Added read() in rpack to eat the EOL character
- * - Added fflush() call in printmsg to force the output
- * NOTE: The last three changes are not conditionally compiled
- * since they should work equally well on any system.
- *
- * Changed Berkeley 4.x conditional compilation flag from
- * UNIX4X to UCB4X.
- * Added support for error packets and cleaned up the printing
- * routines.
- *
- * Feb. '84 Multiple command execution for UCB4X by David Ragozin (UW)
- *
- * Jul. '85 Ported to Data General MV/UX (hosted on AOS/VS) UNIX.
- *
- */
-
- #include <stdio.h> /* Standard UNIX definitions */
- #include <errno.h> /* error definitions */
-
- /* Conditional compilation for different machines/operating systems */
- /* One and only one of the following lines should be 1 */
-
- #define UCB4X 0 /* Berkeley 4.x UNIX */
- #define TOPS_20 0 /* TOPS-20 */
- #define VAX_VMS 0 /* VAX/VMS (not yet impletmented) */
- #define IBM_UTS 0 /* Amdahl UTS on IBM systems */
- #define MVUX 1 /* Data General MV/UX */
-
- /* Conditional compilation for the different Unix variants */
- /* 0 means don't compile it, nonzero means do */
-
- #if UCB4X
- #define V6_LIBS 0 /* Dont't use retrofit libraries */
- #define NO_FIONREAD 0 /* We have ioctl(FIONREAD,...) for flushinput() */
- #define NO_TANDEM 0 /* We have TANDEM line discipline (xon/xoff) */
- #endif
-
- #if IBM_UTS
- #define V6_LIBS 0 /* Don't use retrofit libraries */
- #define NO_FIONREAD 1 /* No ioctl(FIONREAD,...) for flushinput() */
- #define NO_TANDEM 1 /* No TANDEM line discipline (xon/xoff) */
- #endif
-
- #if MVUX
- #define V6_LIBS 0 /* Don't use retrofit libraries */
- #define NO_FIONREAD 1 /* No ioctl(FIONREAD,...) for flushinput() */
- #define NO_TANDEM 1 /* No TANDEM line discipline (xon/xoff) */
- #define CRMOD 0 /* No CRMOD on MV/UX, so ... */
- #endif
-
- #if V6_LIBS
- #include <retrofit/sgtty.h>
- #include <retrofit/signal.h>
- #include <retrofit/setjmp.h>
- #else
- #include <sgtty.h>
- #include <signal.h>
- #include <setjmp.h>
- #endif /* V6_LIBS */
-
- #if MVUX
- #include <multitask.h>
- #include <termio.h>
- #endif
-
- #if NO_TANDEM
- #define TANDEM 0
- #endif
-
- /*
- ** Manifest constants.
- */
-
- #define MAXPACK 94 /* maximum packet size */
- #define SOH 1 /* start of header */
- #define BELL 7 /* ASCII bell */
- #define CR 13 /* ASCII carriage return */
- #define SP 32 /* ASCII space */
- #define DEL 127 /* delete (rubout) */
- #define ESCCHR '^' /* default escape character */
- #define MAXTRY 10 /* times to retry a packet */
- #define MYQUOTE '#' /* quote character */
- #define MYPAD 0 /* number of pad characters needed */
- #define MYPCHAR 0 /* padding character */
- #define SHELL "/bin/sh" /* default shell to use */
- #define TTY "/dev/tty"
-
- #if IBM_UTS
- #define MYEOL '\r' /* EOL character for UTS */
- #else
- #define MYEOL '\n' /* EOL character for others */
- #endif
-
- #define MYTIME 10 /* timeout interval */
- #define MAXTIM 60 /* maximum timeout interval */
- #define MINTIM 2 /* minimum timeout interval */
- #define CMDLINE BUFSIZ /* maximum command line size */
-
- #define TRUE -1 /* boolean constants */
- #define FALSE 0
-
- #if MVUX
- #define LISTENID 16 /* task id of listener task */
- #define TALKID 17 /* task id of talker task */
- #define LISTENPRI 1 /* priority of listener */
- #define TALKPRI 2 /* priority of talker */
- #define STACK 1024 /* default stack size */
- #endif
-
- /*
- ** Macro definitions.
- */
-
- #define tochar(ch) ((ch) + ' ') /* control character to printable */
- #define unchar(ch) ((ch) - ' ') /* printable to control character */
- #define ctl(ch) ((ch) ^ 64 ) /* toggle control bit */
- #define strip(ch) ((ch) & 0x7f) /* strip parity (eigth) bit */
- #define stty(fd, p) ioctl(fd, TIOCSETP, p)
- #define gtty(fd, p) ioctl(fd, TIOCGETP, p)
-
- /*
- ** Type and structure definitions.
- */
-
- typedef enum _cmdtype
- {
- cmd_connect, /* connect to remote */
- cmd_help, /* print Kermit help */
- cmd_none, /* no valid command */
- cmd_quit, /* exit Kermit */
- cmd_receive, /* receive files */
- cmd_send, /* send files */
- cmd_shell /* UNIX shell command */
- } cmdtype;
-
- /*
- ** Imported functions.
- */
-
- extern int fclose(); /* close stream */
- extern char * fgets(); /* read string from stream */
- extern FILE * fopen(); /* open stream */
- extern char * getenv(); /* get environment variable */
- extern int pclose(); /* close pipe from process */
- extern FILE * popen(); /* open pipe to process */
- extern char * strcpy(); /* copy string */
-
- /*
- ** Local functions.
- */
-
- static void bufemp(); /* empty buffer of data */
- static int bufill(); /* get buffer of data */
- static int clkint(); /* clock handler */
- static int closeline(); /* close communication line */
- static void connect(); /* connect function */
- static void defaults(); /* establish default characteristics */
- static void doconnect(); /* execute a connect command */
- static void dohelp(); /* print help menu */
- static void doreceive(); /* execute a receive command */
- static void dosend(); /* execute a send command */
- static void doshell(); /* execute a shell command */
- static int error(); /* print error message */
- static void flushinput(); /* flush input queue */
- static int getcmdline(); /* get next command line */
- static int mapspeed(); /* map baud rate */
- static int openline(); /* open communications line */
- static cmdtype parsecmd(); /* parse command line */
- static void prerrpkt(); /* print error packet */
- static void printmsg(); /* print message */
- static int recsw(); /* receive file state machine */
- static int rdata(); /* receive file data */
- static int rfile(); /* receive file header */
- static int rinit(); /* receive initialization */
- static int rpack(); /* receive a packet */
- static void rpar(); /* get others send-init parameters */
- static int sbreak(); /* send a break */
- static int sdata(); /* send file data */
- static int sendsw(); /* send file state machine */
- static int seof(); /* send eof */
- static int sfile(); /* send file header */
- static int sinit(); /* send initiate */
- static int spack(); /* send packet */
- static void spar(); /* load send-init parameters */
- static void usage(); /* print usage and quit */
-
- #if MVUX
- static void listener(); /* listener sub-task */
- static void talker(); /* talker sub-task */
- #endif
-
- /*
- ** Exported functions.
- */
-
- int main(); /* program entry point */
-
- /*
- ** Imported data structures.
- */
- /* none */
-
- /*
- ** Local data structures
- */
-
- static FILE * crfp; /* communications read file pointer */
- static FILE * cwfp; /* communications write file pointer */
-
- static int lflag; /* true if line option given */
- static int lopen; /* true if line is open */
- static char llast[BUFSIZ]; /* last line name */
- static char lname[BUFSIZ]; /* line name */
- static FILE * lrfp; /* line read file pointer */
- static FILE * lwfp; /* line write file pointer */
-
- static int topen; /* true if terminal is open */
- static char tname[BUFSIZ]; /* terminal name */
- static FILE * trfp; /* terminal read file pointer */
- static FILE * twfp; /* terminal write file pointer */
-
- static int speed; /* baud rate of communications line */
-
- static int size; /* size of present data */
- static int rpsiz; /* maximum receive packet size */
- static int spsiz; /* maximum send packet size */
- static int pad; /* how much padding to send */
- static int timint; /* timeout for foreign host on sends */
- static int n; /* packet number */
- static int numtry; /* number of retries for this packet */
- static int oldtry; /* times previous packet retried */
- static int terminate; /* termination flag for connect */
- static int remote; /* -1 means we're a remote kermit */
- static int image; /* -1 means 8 bit mode */
- static int debug; /* debugging level */
- static int convert; /* do file name case conversion */
-
- static int file; /* current file number */
- static int files; /* number of files to send */
- static char * filename; /* current file name */
- static char * filenames[256]; /* pointers to file names */
-
- static char state; /* present state of the automaton */
- static char padchar; /* padding character */
- static char eol; /* end of line character to send */
- static char escchr; /* connect command escape char */
- static char quote; /* quote character in incoming data */
- static char recpkt[MAXPACK];/* receive packet buffer */
- static char packet[MAXPACK];/* packet buffer */
- static char shargs[BUFSIZ]; /* arguments to a shell command */
-
- static FILE * fp; /* file pointer for current disk file */
- static jmp_buf env; /* jump buffer for timeouts */
-
- /*
- * m a i n
- *
- * Main routine - Initialize and enter command loop: parse command and options,
- * set up the tty lines, dispatch to the appropriate routine and get next
- * command.
- */
-
- int main(argc, argv)
- int argc; /* argument count */
- char * argv[]; /* argument vector */
- {
- int c; /* current character */
- int ac; /* argument count */
- char * av[256]; /* argument vector */
- char ** avp; /* argument vector pointer */
- cmdtype cmd; /* command */
- char cmdline[BUFSIZ]; /* command line */
-
- /*
- ** Set up so that first iteration uses the command line as
- ** the line to parse. Establish the default values for
- ** certain ``global'' variables.
- */
- ac = argc - 1;
- avp = ++argv;
- defaults();
- /*
- ** Top level loop. This loop obtains until the quit command is
- ** received.
- */
- cmd = cmd_none;
- while (cmd != cmd_quit)
- {
- /*
- ** Parse and execute the current command line.
- */
- cmd = parsecmd(ac, avp);
- /*
- ** In order to support the transfer of files, the communications
- ** lines must be opened and closed in a certain order. The code
- ** that follows is an attempt to do just that.
- */
- if (cmd == cmd_connect)
- {
- remote = 0; /* connect is always local */
- if (lflag) /* if line specified */
- {
- if (lopen) /* if a line is open */
- {
- if (strcmp(lname, llast) != 0)
- {
- closeline(lrfp, lwfp);
- lopen = 0;
- if (openline(lname, &lrfp, &lwfp) == -1)
- cmd = cmd_none;
- else
- {
- strcpy(lname, llast);
- lopen = 1;
- }
- }
- }
- else
- {
- if (openline(lname, &lrfp, &lwfp) == -1)
- {
- lopen = 0;
- cmd = cmd_none;
- }
- else
- {
- strcpy(lname, llast);
- lopen = 1;
- }
- }
- }
- else if (lopen) /* if line already open */
- printmsg("Connection continued on line %s.\n", lname);
- else /* no line open; none given */
- {
- error("No line specified for connect command.\n");
- cmd = cmd_none;
- }
- }
- else if (cmd == cmd_send || cmd == cmd_receive)
- {
- if (lflag) /* if line specified */
- {
- remote = 0; /* local */
- if (lopen) /* if a line is open */
- {
- if (strcmp(lname, llast) != 0)
- {
- closeline(lrfp, lwfp);
- lopen = 0;
- if (openline(lname, &lrfp, &lwfp) == -1)
- cmd = cmd_none;
- else
- {
- strcpy(lname, llast);
- lopen = 1;
- }
- }
- }
- else
- {
- if (openline(lname, &lrfp, &lwfp) == -1)
- {
- lopen = 0;
- cmd = cmd_none;
- }
- else
- {
- strcpy(lname, llast);
- lopen = 1;
- }
- }
- crfp = lrfp;
- cwfp = lwfp;
- }
- else if (lopen) /* open line; use it */
- {
- remote = 0;
- crfp = lrfp;
- cwfp = cwfp;
- }
- else /* no open line, no line given; use tty */
- {
- remote = 1; /* remote */
- if (topen == 0) /* if tty not open */
- {
- if (openline(tname, &trfp, &twfp) == -1)
- cmd = cmd_none;
- }
- crfp = trfp;
- cwfp = twfp;
- }
- }
- /*
- ** Dispatch to the appropriate execution function depending
- ** on the value of cmd.
- */
- switch (cmd)
- {
- case cmd_connect:
- doconnect();
- break;
-
- case cmd_receive:
- doreceive();
- break;
-
- case cmd_send:
- dosend();
- break;
-
- case cmd_shell:
- doshell();
- break;
-
- case cmd_help:
- dohelp();
- break;
-
- case cmd_quit:
- break;
- }
- /*
- ** If the last command was not a quit command then get the next
- ** command line from the user.
- */
- if (cmd != cmd_quit)
- ac = getcmdline(avp = av, cmdline);
- }
- }
-
-
- /*
- * b u f e m p
- *
- * Put data from an incoming packet into a file.
- */
-
- static void bufemp(buffer,len)
- char buffer[]; /* Buffer */
- int len; /* Length */
- {
- int i; /* Counter */
- char t; /* Character holder */
-
- for (i=0; i<len; i++) /* Loop thru the data field */
- {
- t = buffer[i]; /* Get character */
- if (t == MYQUOTE) /* Control quote? */
- { /* Yes */
- t = buffer[++i]; /* Get the quoted character */
- if ((t & 0177) != MYQUOTE) /* Low order bits match quote char? */
- t = ctl(t); /* No, uncontrollify it */
- }
- if (t==CR && !image) /* Don't pass CR if in image mode */
- continue;
-
- putc(t,fp);
- }
- }
-
-
- /*
- * b u f i l l
- *
- * Get a bufferful of data from the file that's being sent.
- * Only control-quoting is done; 8-bit & repeat count prefixes are
- * not handled.
- */
-
- static int bufill(buffer)
- char buffer[]; /* Buffer */
- {
- int i, /* Loop index */
- t; /* Char read from file */
- char t7; /* 7-bit version of above */
-
- i = 0; /* Init data buffer pointer */
- while((t = getc(fp)) != EOF) /* Get the next character */
- {
- t7 = t & 0177; /* Get low order 7 bits */
-
- if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */
- { /* special handling? */
- if (t=='\n' && !image)
- { /* Do LF->CRLF mapping if !image */
- buffer[i++] = quote;
- buffer[i++] = ctl('\r');
- }
- buffer[i++] = quote; /* Quote the character */
- if (t7 != quote)
- {
- t = ctl(t); /* and uncontrolify */
- t7 = ctl(t7);
- }
- }
- if (image)
- buffer[i++] = t; /* Deposit the character itself */
- else
- buffer[i++] = t7;
-
- if (i >= spsiz-8) return(i); /* Check length */
- }
- if (i==0) return(EOF); /* Wind up here only on EOF */
- return(i); /* Handle partial buffer */
- }
-
-
- /*
- * c l k i n t
- *
- * Clock interrupt handler.
- */
-
- static int clkint() /* Timer interrupt handler */
- {
- longjmp(env,TRUE); /* Tell rpack to give up */
- }
-
-
- /*
- * c l o s e l i n e
- *
- * Function closeline() closes the given file pointers. If an
- * error is detected then a diagnostic is written and -1 is returned.
- * If no error is detected then 0 is returned.
- */
-
- static int closeline(rfp, wfp)
- FILE * rfp; /* read file pointer */
- FILE * wfp; /* write file pointer */
- {
-
- if (fclose(rfp) == -1)
- {
- error("Can't close read file pointer.");
- error("Error code: %d.", errno);
- return -1;
- }
- if (fclose(wfp) == -1)
- {
- error("Can't close write file pointer.");
- error("Error code: %d.", errno);
- return -1;
- }
- return 0;
- }
-
-
- /*
- * c o n n e c t
- *
- * Establish a virtual terminal connection with the remote host.
- */
-
- static void connect()
- {
-
- /*
- ** Under MV/UX a true fork() call does not exist; however, we can
- ** create multiple asynchronous tasks with a program. We create
- ** two sub-tasks, ``listener'' and ``talker'' to handle the data
- ** communication. For the main task to regain control one or both
- ** of the tasks must set the terminate flag; therefore it is cleared
- ** before either task is initiated.
- */
- terminate = 0;
- if (mtask(listener, STACK, LISTENID, LISTENPRI) != 0)
- {
- error("Can't initiate listener task.");
- error("Error code is: %d.\n", errno);
- return;
- }
- if (mtask(talker, STACK, TALKID, TALKPRI) != 0)
- {
- error("Can't initiate talker task.");
- error("Error code is: %d.\n", errno);
- return;
- }
- /*
- ** After creating the sub-tasks the main task (this thread)
- ** enters the following loop. The main task runs once every
- ** two seconds; on each iteration it checks the ``terminate''
- ** flag. When it finds the terminate flag set it pops out of
- ** the loop and terminates the ``listener'' and ``talker''
- ** sub-tasks.
- */
- while (terminate == 0)
- sleep(2);
- if (midkill(LISTENID) == -1)
- {
- error("Can't terminate listener task.");
- return;
- }
- if (midkill(TALKID) == -1)
- {
- error("Can't terminate talker task.");
- return;
- }
- }
-
-
- /*
- * d e f a u l t s
- *
- * Establish default values for the myriad global variables.
- *
- */
-
- static void defaults()
- {
-
- #if UCB4X | MVUX /* Default to 7-bit masking, CRLF */
- image = FALSE; /* translation and filename case */
- convert++; /* conversion for UNIX systems */
- #else
- image = TRUE; /* Default to no processing for */
- convert = FALSE; /* non-UNIX systems */
- #endif
- eol = CR; /* eol for outgoing packets */
- quote = '#'; /* for quoting control characters */
- pad = 0; /* no padding */
- padchar = 0; /* pad with ASCII nul */
- escchr = ESCCHR; /* default escape character */
- strcpy(lname, ""); /* no line name */
- strcpy(llast, ""); /* no default either */
- strcpy(tname, TTY); /* terminal name */
- }
-
-
- /*
- * d o c o n n e c t
- *
- * Top level control for connect command.
- *
- */
-
- static void doconnect()
- {
-
- /*
- ** Open the communications line to the terminal. This line can be
- ** closed when the connect command completes.
- */
- if (openline(tname, &trfp, &twfp) == -1)
- return;
- /*
- ** Inform the user that the connection is established and how
- ** to return. Then invoke connect() to actually manage the
- ** connection.
- */
- printf("Connected. Use %cc to return to command level.\n", escchr);
- connect();
- printf("Disconnected.\n");
- /*
- ** Close the line to the terminal.
- */
- (void) closeline(trfp, twfp);
- }
-
- /*
- * d o h e l p
- *
- * Print a simple help menu on the terminal.
- *
- */
-
- static void dohelp()
- {
-
- /*
- ** Print a help screen for the user.
- */
- printf("\nKermit Help:\n\n");
- printf("When you receive the prompt 'Kermit: ' you are expected to\n");
- printf("reply with a command. The following commands are supported:\n\n");
- printf("\tc[lbe line baud escape] - connect to remote.\n");
- printf("\ts[diflb line baud escape] file(s) - send file(s).\n");
- printf("\tr[diflb line baud escape] - receive file(s).\n");
- printf("\t! [command] - shell escape.\n");
- printf("\t? - this help screen.\n");
- printf("\tq - quit.\n\n");
- #if 0
- printf("Commands: c[lbe line baud esc.char] (to reconnect)\n");
- printf("or: s[diflb line baud] file ... (to send file(s))\n");
- printf("or: r[diflb line baud] (to receive )\n");
- printf("or: ![cmd] (for shell escape)\n");
- printf("or: q (TO QUIT)\n");
- #endif
- #if 0
- printf("Usage: kermit c[le line esc.char] (connect mode)\n");
- printf("or: kermit s[difl line] file ... (send mode)\n");
- printf("or: kermit r[difl line] (receive mode)\n");
- exit(1);
- #endif
- }
-
-
- /*
- * d o r e c e i v e
- *
- * Top level control for receive command.
- *
- */
-
- static void doreceive()
- {
-
- /*
- ** Do the receive and print the resulting status.
- */
- if (recsw() == FALSE)
- printmsg("Receive failed.");
- else
- printmsg("Receive completed.");
- }
-
-
- /*
- * d o s e n d
- *
- * Top level control for send command.
- *
- */
-
- static void dosend()
- {
-
- /*
- ** Get the filename and do the send.
- */
- filename = filenames[file++];
- fp = NULL; /* Indicate no file open yet */
- if (sendsw() == FALSE) /* Send the file(s) */
- printmsg("Send failed.");/* Report failure */
- else /* or */
- printmsg("Done."); /* success */
- }
-
-
- /*
- * d o s h e l l
- *
- * Function doshell() runs a shell command for the user.
- *
- */
-
- static void doshell()
- {
- int args; /* true if args to shell */
- char * s; /* work pointer */
- int (* savehup)(); /* old SIGHUP value */
- int (* saveint)(); /* old SIGINT value */
- int (* savequit)(); /* old SIGQUIT value */
- char path[BUFSIZ]; /* pathname to shell */
- char * base; /* pointer to base name of shell */
- int code; /* return code from wait() */
- int pid; /* child process id */
-
- /*
- ** Get the shell to use. The full pathname to the shell is kept in
- ** path; a pointer, base, is used to point at the basename of the
- ** shell. Finally, scan the shargs string to decide if any arguments
- ** were given.
- */
- if (getenv("SHELL") != NULL)
- strcpy(path, getenv("SHELL"));
- else
- strcpy(path, SHELL);
- for (s = base = path; *s; )
- if (*s++ == '/')
- base = s;
- for (s = shargs, args = 0; *s; s++)
- if (*s != ' ' && *s != '\t')
- args = 1;
- /*
- ** Save the current values of SIGHUP, SIGINT and SIGQUIT.
- ** Then execute the shell command line. Finally, restore
- ** signal handling.
- */
- savehup = signal(SIGHUP, SIG_IGN);
- saveint = signal(SIGINT, SIG_IGN);
- savequit = signal(SIGQUIT, SIG_IGN);
- /*
- ** The child does an execl() call. If the call fails then
- ** a diagnostic is written and the child exits().
- */
- if ((pid = vfork()) == 0) /* child */
- {
- if (args)
- execl(path, base, "-c", shargs, 0);
- else
- execl(path, base, 0);
- error("doshell: execl() has failed.");
- _exit(1);
- }
- /*
- ** The parent waits for the child to terminate, then restores
- ** signals.
- */
- else /* parent */
- {
- while (wait(&code) != pid)
- ;
- (void) signal(SIGHUP, savehup);
- (void) signal(SIGINT, saveint);
- (void) signal(SIGQUIT, savequit);
- /*
- ** Clean up the argument list for the next pass, if any.
- ** Write a new-line to stdout so that Kermit prompt is positioned
- ** correctly.
- */
- strcpy(shargs, "");
- fputs("\n", stdout);
- }
- }
-
-
- /*
- * e r r o r
- *
- * Print error message.
- *
- * If local, print error message with printmsg.
- * If remote, send an error packet with the message.
- */
-
- /*VARARGS1*/
- static int error(fmt, a1, a2, a3, a4, a5)
- char *fmt;
- {
- char msg[80];
- int len;
-
- if (remote)
- {
- sprintf(msg,fmt,a1,a2,a3,a4,a5); /* Make it a string */
- len = strlen(msg);
- spack('E',n,len,msg); /* Send the error packet */
- }
- else
- printmsg(fmt, a1, a2, a3, a4, a5);
- }
-
-
- /*
- * f l u s h i n p u t
- *
- * Dump all pending input to clear stacked up NACK's.
- * (Implemented only for Berkeley Unix at this time).
- */
-
- #if UCB4X&(~NO_FIONREAD)
- static void flushinput()
- {
- long int count; /* Number of bytes ready to read */
- long int i; /* Number of bytes to read in loop */
-
- ioctl(ttyfd, FIONREAD, &count); /* See how many bytes pending read */
- if (!count) return; /* If zero, then no input to flush */
-
- while (count) /* Loop till all are flushed */
- {
- i = (count<sizeof(recpkt)) ? /* Read min of count and size of */
- count : sizeof(recpkt); /* the read buffer */
- read(ttyfd, recpkt, i); /* Read a bunch */
- count -= i; /* Subtract from amount to read */
- }
- }
- #else
- static void flushinput() /* Null version for non-Berkeley Unix */
- {}
- #endif /* UCB4X&(~FIONREAD) */
-
-
- /*
- * g e t c m d l i n e
- *
- * This function gets the next Kermit command line from the users
- * terminal. The Kermit prompt is written and a single line is
- * read. The line is then expanded by 'echo'ing it through a shell.
- * Once expanded, the arguments, ac and av, are set up as if by
- * the C start-off routines. This function returns the number of
- * arguments on the command line.
- */
-
- static int getcmdline(av, cmdline)
- char * av[]; /* argument vector */
- char * cmdline; /* returned command line */
- {
- int ac; /* argument count */
- char * s; /* work pointer */
- char buf[BUFSIZ]; /* work buffer */
- int length; /* command length */
- FILE * pfp; /* for popen() / pclose() */
-
- /*
- ** Print the Kermit prompt and read the line typed by the user.
- ** If the users responds with ^D then hand-craft a quit command.
- ** Clip off the delimeter, usually \n.
- */
- printf("Kermit: ");
- if (fgets(cmdline, CMDLINE, stdin) == NULL)
- {
- printf("[EOT]\n");
- strcpy(cmdline, "q\n");
- }
- if ((length = strlen(s = cmdline)) > 0)
- cmdline[length - 1] = 0;
- /*
- ** Hand-craft a command line to expand the command line. Then
- ** use popen() / pclose() to get the results of the expansion.
- */
- #if 0
- sprintf(buf, "/bin/echo %s", cmdline);
- if ((pfp = popen(buf, "r")) == NULL)
- {
- perror("getcmdline: popen");
- exit(1);
- }
- if ((s = fgets(cmdline, CMDLINE, pfp)) == NULL)
- {
- perror("getcmdline: fgets");
- exit(1);
- }
- if (pclose(pfp) == -1)
- {
- perror("getcmdline: pclose");
- exit(1);
- }
- #endif
- /*
- ** Finally, set up ac and av. Note that each argument in cmdline
- ** is terminated with a NULL so that it looks just like a 'real'
- ** argv from C runtimes.
- */
- while (*s == ' ' || *s == '\t') /* skip whitespace */
- s++;
- for (av[ac = 0] = NULL; *s; ac++)
- {
- av[ac] = s; /* point to argument */
- while (*s && *s != ' ' && *s != '\t') /* skip argument body */
- s++;
- if (*s) /* if more arguments */
- *s++ = 0;
- while (*s == ' ' || *s == '\t') /* skip whitespace */
- s++;
- }
- av[ac] = NULL;
- return ac;
- }
-
-
- #if MVUX
- /*
- * l i s t e n e r
- *
- * Special MV/UX sub-task to listen for characters from the remote system.
- */
-
- static void listener()
- {
- int cc; /* I/O buffer */
-
- while (1)
- {
- cc = fgetc(lrfp);
- fputc(cc, twfp);
- }
- }
- #endif
-
-
- /*
- * m a p s p e e d
- *
- * Map an integer baud rate (300, 1200, etc.) to the appropriate manifest
- * value (B300, B1200, etc.). Returns manifest or -1 for illegal baud.
- */
-
- static int mapspeed(baud)
- int baud; /* input baud rate */
- {
- int manifest; /* manifest value */
-
- switch (baud)
- {
- case 110:
- manifest = B110;
- break;
-
- case 150:
- manifest = B150;
- break;
-
- case 300:
- manifest = B300;
- break;
-
- case 1200:
- manifest = B1200;
- break;
-
- case 2400:
- manifest = B2400;
- break;
-
- case 4800:
- manifest = B4800;
- break;
-
- case 9600:
- manifest = B9600;
- break;
-
- default: /* error case */
- manifest = -1;
- break;
- }
- return manifest;
- }
-
-
- /*
- * o p e n l i n e
- *
- * Function openline() opens read and write file pointers to the
- * given file, assumed to be a communications line. If an error
- * is detected then a diagnostic is written and -1 is returned.
- * On no error, 0 is returned.
- *
- */
-
- static int openline(name, rfp, wfp)
- char * name; /* device name to open */
- FILE ** rfp; /* read file pointer */
- FILE ** wfp; /* write file pointer */
- {
- #if MVUX
- char * rmode = "j"; /* DG binary read */
- char * wmode = "k"; /* DG binary write */
- #else
- char * rmode = "r"; /* normal read */
- char * wmode = "w"; /* normal write */
- #endif
-
- if ((*rfp = fopen(name, rmode)) == NULL)
- {
- error("Can't open %s for mode %s.", name, rmode);
- error("Error code: %d.", errno);
- return -1;
- }
- if ((*wfp = fopen(name, wmode)) == NULL)
- {
- error("Can't open %s for mode %s.", name, wmode);
- error("Error code: %d.", errno);
- return -1;
- }
- setbuf(*rfp, NULL);
- setbuf(*wfp, NULL);
- return 0;
- }
-
-
- /*
- * p a r s e c m d
- *
- * Function parsecmd() does a simple minded parse of the given command
- * line. While it would be nice to conform the UNIX standard and use
- * getopt() to parse this is not done. The change would like cause more
- * conflict than good. Sigh.
- *
- * If the command line does not contain a Kermit command letter this
- * function returns ``cmd_none''.
- *
- */
-
- static cmdtype parsecmd(ac, av)
- int ac; /* argument count */
- char * av[]; /* argument vector */
- {
- int c; /* current character */
- cmdtype cmd; /* current command */
- int index; /* current argument index */
- char * s; /* work pointer */
-
- /*
- ** The default Kermit standard for command lines requires that the
- ** first argument begin with a command, then optional arguments.
- ** Check that this is the case, returning cmd_none in the case of
- ** an empty command line.
- */
- cmd = cmd_none;
- if (ac == 0) /* empty command line */
- return cmd;
- /*
- ** Parse the first argument. This is the command plus any of the
- ** options.
- */
- remote = 0; /* default to local */
- s = av[index = 0];
- switch (c = *s++)
- {
- case 'c': /* connect command */
- cmd = cmd_connect;
- break;
-
- case 'r': /* receive command */
- cmd = cmd_receive;
- remote = 1; /* default to remote */
- break;
-
- case 's': /* send command */
- cmd = cmd_send;
- remote = 1; /* default to remote */
- break;
-
- case '!': /* shell command */
- cmd = cmd_shell;
- while (av[++index] != NULL)
- {
- strcat(shargs, " ");
- strcat(shargs, av[index]);
- }
- break;
-
- case '?': /* help command */
- cmd = cmd_help;
- break;
-
- case 'q': /* quit command */
- cmd = cmd_quit;
- break;
-
- default: /* unknown command */
- error("Not a valid command: '%c'. Use '?' for help.", c);
- return cmd_none;
- break;
- }
- /*
- ** If the command was one of [c,r,s] then continue parsing for options.
- ** Otherwise return immediately to caller.
- */
- if (cmd == cmd_connect || cmd == cmd_receive || cmd == cmd_send)
- {
- while (*s) /* while more arguments */
- {
- switch (c = *s++)
- {
- case 'd':
- debug++; /* debug */
- break;
-
- case 'i': /* image mode */
- image = 1;
- break;
-
- case 'f': /* convert file names */
- convert = 1;
- break;
-
- case 'b': /* baud rate (speed) */
- if (av[++index] == NULL)
- {
- error("Option 'b' requires baud rate.");
- return cmd_none;
- }
- speed = atoi(av[index]);
- break;
-
- case 'e':
- if (av[++index] == NULL)
- {
- error("Option 'e' requires escape character.");
- return cmd_none;
- }
- escchr = av[index][0];
- break;
-
- case 'l': /* line */
- lflag = 1;
- remote = 0;
- if (av[++index] == NULL)
- {
- error("Option 'l' requires a line name.");
- return cmd_none;
- }
- strcpy(lname, av[index]);
- strcpy(llast, av[index]);
- break;
-
- default:
- error("Not a valid option: '%c'. Use ? for help.", c);
- return cmd_help;
- break;
- }
- }
- /*
- ** Now, if the command was cmd_send, pick up all files.
- */
- if (cmd == cmd_send)
- {
- for (files = 0, index++; av[index] != NULL; files++, index++)
- filenames[files] = av[index];
- if (files == 0)
- {
- error("No files specified for send command.");
- return cmd_help;
- }
- }
- }
- return cmd;
- }
-
-
- /*
- * p r e r r p k t
- *
- * Print contents of error packet received from remote host.
- */
-
- static void prerrpkt(msg)
- char * msg;
- {
- printf("Kermit aborting with following error from remote host:\n%s\n",msg);
- }
-
-
- /*
- * p r i n t m s g
- *
- * Print error message on standard output if not remote.
- */
-
- /*VARARGS1*/
- static void printmsg(fmt, a1, a2, a3, a4, a5)
- char * fmt;
- {
- if (! remote)
- {
- printf(fmt, a1, a2, a3, a4, a5);
- printf("\n");
- fflush(stdout); /* force output (UTS needs it) */
- }
- }
-
-
- /*
- * r d a t a
- *
- * Receive Data
- */
-
- static int rdata()
- {
- int num, len; /* Packet number, length */
- if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */
-
- switch(rpack(&len,&num,packet)) /* Get packet */
- {
- case 'D': /* Got Data packet */
- if (num != n) /* Right packet? */
- { /* No */
- if (oldtry++ > MAXTRY)
- return('A'); /* If too many tries, abort */
- if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
- { /* Previous packet again? */
- spack('Y',num,6,packet); /* Yes, re-ACK it */
- numtry = 0; /* Reset try counter */
- return(state); /* Don't write out data! */
- }
- else return('A'); /* sorry, wrong number */
- }
- /* Got data with right packet number */
- bufemp(packet,len); /* Write the data to the file */
- spack('Y',n,0,0); /* Acknowledge the packet */
- oldtry = numtry; /* Reset the try counters */
- numtry = 0; /* ... */
- n = (n+1)%64; /* Bump packet number, mod 64 */
- return('D'); /* Remain in data state */
-
- case 'F': /* Got a File Header */
- if (oldtry++ > MAXTRY)
- return('A'); /* If too many tries, "abort" */
- if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
- { /* It was the previous one */
- spack('Y',num,0,0); /* ACK it again */
- numtry = 0; /* Reset try counter */
- return(state); /* Stay in Data state */
- }
- else return('A'); /* Not previous packet, "abort" */
-
- case 'Z': /* End-Of-File */
- if (num != n) return('A'); /* Must have right packet number */
- spack('Y',n,0,0); /* OK, ACK it. */
- fclose(fp); /* Close the file */
- n = (n+1)%64; /* Bump packet number */
- return('F'); /* Go back to Receive File state */
-
- case 'E': /* Error packet received */
- prerrpkt(recpkt); /* Print it out and */
- return('A'); /* abort */
-
- case FALSE: /* Didn't get packet */
- spack('N',n,0,0); /* Return a NAK */
- return(state); /* Keep trying */
-
- default: return('A'); /* Some other packet, "abort" */
- }
- }
-
-
- /*
- * r e c s w
- *
- * This is the state table switcher for receiving files.
- */
-
- static int recsw()
- {
-
- state = 'R'; /* Receive-Init is the start state */
- n = 0; /* Initialize message number */
- numtry = 0; /* Say no tries yet */
-
- while(TRUE)
- {
- if (debug) printf(" recsw state: %c\n",state);
- switch(state) /* Do until done */
- {
- case 'R': state = rinit(); break; /* Receive-Init */
- case 'F': state = rfile(); break; /* Receive-File */
- case 'D': state = rdata(); break; /* Receive-Data */
- case 'C': return(TRUE); /* Complete state */
- case 'A': return(FALSE); /* "Abort" state */
- }
- }
- }
-
-
- /*
- * r f i l e
- *
- * Receive File Header
- */
-
- static int rfile()
- {
- int num, len; /* Packet number, length */
- char filnam1[50]; /* Holds the converted file name */
-
- if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */
-
- switch(rpack(&len,&num,packet)) /* Get a packet */
- {
- case 'S': /* Send-Init, maybe our ACK lost */
- if (oldtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
- if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
- { /* Yes, ACK it again with */
- spar(packet); /* our Send-Init parameters */
- spack('Y',num,6,packet);
- numtry = 0; /* Reset try counter */
- return(state); /* Stay in this state */
- }
- else return('A'); /* Not previous packet, "abort" */
-
- case 'Z': /* End-Of-File */
- if (oldtry++ > MAXTRY) return('A');
- if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
- { /* Yes, ACK it again. */
- spack('Y',num,0,0);
- numtry = 0;
- return(state); /* Stay in this state */
- }
- else return('A'); /* Not previous packet, "abort" */
-
- case 'F': /* File Header (just what we want) */
- if (num != n) return('A'); /* The packet number must be right */
- strcpy(filnam1, packet); /* Copy the file name */
-
- if (convert) /* Convert upper case to lower */
- for (filename=filnam1; *filename != '\0'; filename++)
- if (*filename >= 'A' && *filename <= 'Z')
- *filename |= 040;
-
- if ((fp=fopen(filnam1,"w"))==NULL) /* Try to open a new file */
- {
- error("cannot create %s",filnam1); /* Give up if can't */
- return('A');
- }
- else /* OK, give message */
- printmsg("Receiving %s as %s",packet,filnam1);
-
- spack('Y',n,0,0); /* Acknowledge the file header */
- oldtry = numtry; /* Reset try counters */
- numtry = 0; /* ... */
- n = (n+1)%64; /* Bump packet number, mod 64 */
- return('D'); /* Switch to Data state */
-
- case 'B': /* Break transmission (EOT) */
- if (num != n) return ('A'); /* Need right packet number here */
- spack('Y',n,0,0); /* Say OK */
- return('C'); /* Go to complete state */
-
- case 'E': /* Error packet received */
- prerrpkt(recpkt); /* Print it out and */
- return('A'); /* abort */
-
- case FALSE: /* Didn't get packet */
- spack('N',n,0,0); /* Return a NAK */
- return(state); /* Keep trying */
-
- default: return ('A'); /* Some other packet, "abort" */
- }
- }
-
-
- /*
- * r i n i t
- *
- * Receive Initialization
- */
-
- static int rinit()
- {
- int len, num; /* Packet length, number */
-
- if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
-
- switch(rpack(&len,&num,packet)) /* Get a packet */
- {
- case 'S': /* Send-Init */
- rpar(packet); /* Get the other side's init data */
- spar(packet); /* Fill up packet with my init info */
- spack('Y',n,6,packet); /* ACK with my parameters */
- oldtry = numtry; /* Save old try count */
- numtry = 0; /* Start a new counter */
- n = (n+1)%64; /* Bump packet number, mod 64 */
- return('F'); /* Enter File-Receive state */
-
- case 'E': /* Error packet received */
- prerrpkt(recpkt); /* Print it out and */
- return('A'); /* abort */
-
- case FALSE: /* Didn't get packet */
- spack('N',n,0,0); /* Return a NAK */
- return(state); /* Keep trying */
-
- default: return('A'); /* Some other packet type, "abort" */
- }
- }
-
-
- /*
- * r p a c k
- *
- * Read a Packet
- */
-
- static int rpack(len,num,data)
- int *len, *num; /* Packet length, number */
- char *data; /* Packet data */
- {
- int i, done; /* Data character number, loop exit */
- char t, /* Current input character */
- type, /* Packet type */
- cchksum, /* Our (computed) checksum */
- rchksum; /* Checksum received from other host */
-
- #if UCB4X | MVUX /* TOPS-20 can't handle timeouts... */
- if (setjmp(env)) return FALSE; /* Timed out, fail */
- signal(SIGALRM,clkint); /* Setup the timeout */
- if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
- alarm(timint);
- #endif /* UCB4X */
-
- while (t != SOH) /* Wait for packet header */
- {
- t = fgetc(crfp);
- t &= 0177; /* Handle parity */
- }
-
- done = FALSE; /* Got SOH, init loop */
- while (!done) /* Loop to get a packet */
- {
- t = fgetc(crfp); /* Get Character */
- if (!image) t &= 0177; /* Handle parity */
- if (t == SOH) continue; /* Resynchronize if SOH */
- cchksum = t; /* Start the checksum */
- *len = unchar(t)-3; /* Character count */
-
- t = fgetc(crfp); /* Get character */
- if (!image) t &= 0177; /* Handle parity */
- if (t == SOH) continue; /* Resynchronize if SOH */
- cchksum = cchksum + t; /* Update checksum */
- *num = unchar(t); /* Packet number */
-
- t = fgetc(crfp); /* Get character */
- if (!image) t &= 0177; /* Handle parity */
- if (t == SOH) continue; /* Resynchronize if SOH */
- cchksum = cchksum + t; /* Update checksum */
- type = t; /* Packet type */
-
- for (i=0; i<*len; i++) /* The data itself, if any */
- { /* Loop for character count */
- t = fgetc(crfp); /* Get character */
- if (!image) t &= 0177; /* Handle parity */
- if (t == SOH) continue; /* Resynch if SOH */
- cchksum = cchksum + t; /* Update checksum */
- data[i] = t; /* Put it in the data buffer */
- }
- data[*len] = 0; /* Mark the end of the data */
-
- t = fgetc(crfp); /* Get last character (checksum) */
- rchksum = unchar(t); /* Convert to numeric */
- t = fgetc(crfp); /* Get EOL character and toss it */
- if (!image) t &= 0177; /* Handle parity */
- if (t == SOH) continue; /* Resynchronize if SOH */
- done = TRUE; /* Got checksum, done */
- }
-
- #if UCB4X | MVUX
- alarm(0); /* Disable the timer interrupt */
- #endif
-
- if (debug>1) /* Display incoming packet */
- {
- if (data != NULL)
- data[*len] = '\0'; /* Null-terminate data to print it */
- printf(" rpack type: %c\n",type);
- printf(" num: %d\n",*num);
- printf(" len: %d\n",*len);
- if (data != NULL)
- printf(" data: \"%s\"\n",data);
- }
- /* Fold in bits 7,8 to compute */
- cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */
-
- if (cchksum != rchksum) return(FALSE);
-
- return(type); /* All OK, return packet type */
- }
-
-
- /* r p a r
- *
- * Get the other host's send-init parameters
- *
- */
-
- static void rpar(data)
- char data[];
- {
- spsiz = unchar(data[0]); /* Maximum send packet size */
- timint = unchar(data[1]); /* When I should time out */
- pad = unchar(data[2]); /* Number of pads to send */
- padchar = ctl(data[3]); /* Padding character to send */
- eol = unchar(data[4]); /* EOL character I must send */
- quote = data[5]; /* Incoming data quote character */
- }
-
-
- /*
- * s b r e a k
- *
- * Send Break (EOT)
- */
-
- static int sbreak()
- {
- int num, len; /* Packet number, length */
- if (numtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
-
- spack('B',n,0,packet); /* Send a B packet */
- switch (rpack(&len,&num,recpkt)) /* What was the reply? */
- {
- case 'N': /* NAK, just stay in this state, */
- num = (--num<0 ? 63:num); /* unless NAK for previous packet, */
- if (n != num) /* which is just like an ACK for */
- return(state); /* this packet so fall thru to... */
-
- case 'Y': /* ACK */
- if (n != num) return(state); /* If wrong ACK, fail */
- numtry = 0; /* Reset try counter */
- n = (n+1)%64; /* and bump packet count */
- return('C'); /* Switch state to Complete */
-
- case 'E': /* Error packet received */
- prerrpkt(recpkt); /* Print it out and */
- return('A'); /* abort */
-
- case FALSE: return(state); /* Receive failure, stay in B */
-
- default: return ('A'); /* Other, "abort" */
- }
- }
-
-
- /*
- * s d a t a
- *
- * Send File Data
- */
-
- static int sdata()
- {
- int num, len; /* Packet number, length */
-
- if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
-
- spack('D',n,size,packet); /* Send a D packet */
- switch(rpack(&len,&num,recpkt)) /* What was the reply? */
- {
- case 'N': /* NAK, just stay in this state, */
- num = (--num<0 ? 63:num); /* unless it's NAK for next packet */
- if (n != num) /* which is just like an ACK for */
- return(state); /* this packet so fall thru to... */
-
- case 'Y': /* ACK */
- if (n != num) return(state); /* If wrong ACK, fail */
- numtry = 0; /* Reset try counter */
- n = (n+1)%64; /* Bump packet count */
- if ((size = bufill(packet)) == EOF) /* Get data from file */
- return('Z'); /* If EOF set state to that */
- return('D'); /* Got data, stay in state D */
-
- case 'E': /* Error packet received */
- prerrpkt(recpkt); /* Print it out and */
- return('A'); /* abort */
-
- case FALSE: return(state); /* Receive failure, stay in D */
-
- default: return('A'); /* Anything else, "abort" */
- }
- }
-
-
- /*
- * s e n d s w
- *
- * Sendsw is the state table switcher for sending files. It loops until
- * either it finishes, or an error is encountered. The routines called
- * by sendsw are responsible for changing the state.
- *
- */
-
- static int sendsw()
- {
-
- state = 'S'; /* Send initiate is the start state */
- n = 0; /* Initialize message number */
- numtry = 0; /* Say no tries yet */
- while(TRUE) /* Do this as long as necessary */
- {
- if (debug) printf("sendsw state: %c\n",state);
- switch(state)
- {
- case 'S': state = sinit(); break; /* Send-Init */
- case 'F': state = sfile(); break; /* Send-File */
- case 'D': state = sdata(); break; /* Send-Data */
- case 'Z': state = seof(); break; /* Send-End-of-File */
- case 'B': state = sbreak(); break; /* Send-Break */
- case 'C': return (TRUE); /* Complete */
- case 'A': return (FALSE); /* "Abort" */
- default: return (FALSE); /* Unknown, fail */
- }
- }
- }
-
-
- /*
- * s e o f
- *
- * Send End-Of-File.
- */
-
- static int seof()
- {
- int num, len; /* Packet number, length */
- if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
-
- spack('Z',n,0,packet); /* Send a 'Z' packet */
- switch(rpack(&len,&num,recpkt)) /* What was the reply? */
- {
- case 'N': /* NAK, just stay in this state, */
- num = (--num<0 ? 63:num); /* unless it's NAK for next packet, */
- if (n != num) /* which is just like an ACK for */
- return(state); /* this packet so fall thru to... */
-
- case 'Y': /* ACK */
- if (n != num) return(state); /* If wrong ACK, hold out */
- numtry = 0; /* Reset try counter */
- n = (n+1)%64; /* and bump packet count */
- if (debug) printf(" Closing input file %s, ", filename);
- fclose(fp); /* Close the input file */
- fp = NULL; /* Set flag indicating no file open */
-
- if (debug) printf("looking for next file...\n");
- if (file == files) /* Any more files ? */
- return('B'); /* No */
- filename = filenames[file++];
- if (debug) printf(" New file is %s\n", filename);
- return('F'); /* More files, switch state to F */
-
- case 'E': /* Error packet received */
- prerrpkt(recpkt); /* Print it out and */
- return('A'); /* abort */
-
- case FALSE: return(state); /* Receive failure, stay in Z */
-
- default: return('A'); /* Something else, "abort" */
- }
- }
-
-
- /*
- * s f i l e
- *
- * Send File Header.
- */
-
- static int sfile()
- {
- int num, len; /* Packet number, length */
- char filnam1[50], /* Converted file name */
- *newfilnam, /* Pointer to file name to send */
- *cp; /* char pointer */
-
- if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
-
- if (fp == NULL) /* If not already open, */
- { if (debug) printf(" Opening %s for sending.\n", filename);
- fp = fopen(filename,"r"); /* open the file to be sent */
- if (fp == NULL) /* If bad file pointer, give up */
- {
- error("Cannot open file %s", filename);
- return('A');
- }
- }
-
- strcpy(filnam1, filename); /* Copy file name */
- newfilnam = cp = filnam1;
- while (*cp != '\0') /* Strip off all leading directory */
- if (*cp++ == '/') /* names (ie. up to the last /). */
- newfilnam = cp;
-
- if (convert) /* Convert lower case to upper */
- for (cp = newfilnam; *cp != '\0'; cp++)
- if (*cp >= 'a' && *cp <= 'z')
- *cp ^= 040;
-
- len = cp - newfilnam; /* Compute length of new filename */
-
- printmsg("Sending %s as %s",filename,newfilnam);
-
- spack('F',n,len,newfilnam); /* Send an F packet */
- switch(rpack(&len,&num,recpkt)) /* What was the reply? */
- {
- case 'N': /* NAK, just stay in this state, */
- num = (--num<0 ? 63:num); /* unless it's NAK for next packet */
- if (n != num) /* which is just like an ACK for */
- return(state); /* this packet so fall thru to... */
-
- case 'Y': /* ACK */
- if (n != num) return(state); /* If wrong ACK, stay in F state */
- numtry = 0; /* Reset try counter */
- n = (n+1)%64; /* Bump packet count */
- size = bufill(packet); /* Get first data from file */
- return('D'); /* Switch state to D */
-
- case 'E': /* Error packet received */
- prerrpkt(recpkt); /* Print it out and */
- return('A'); /* abort */
-
- case FALSE: return(state); /* Receive failure, stay in F state */
-
- default: return('A'); /* Something else, just "abort" */
- }
- }
-
-
- /*
- * s i n i t
- *
- * Send Initiate: send this host's parameters and get other side's back.
- */
-
- static int sinit()
- {
- int num, len; /* Packet number, length */
-
- if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
- spar(packet); /* Fill up init info packet */
-
- flushinput(); /* Flush pending input */
-
- spack('S',n,6,packet); /* Send an S packet */
- switch(rpack(&len,&num,recpkt)) /* What was the reply? */
- {
- case 'N': return(state); /* NAK, try it again */
-
- case 'Y': /* ACK */
- if (n != num) /* If wrong ACK, stay in S state */
- return(state); /* and try again */
- rpar(recpkt); /* Get other side's init info */
-
- if (eol == 0) eol = '\n'; /* Check and set defaults */
- if (quote == 0) quote = '#';
-
- numtry = 0; /* Reset try counter */
- n = (n+1)%64; /* Bump packet count */
- return('F'); /* OK, switch state to F */
-
- case 'E': /* Error packet received */
- prerrpkt(recpkt); /* Print it out and */
- return('A'); /* abort */
-
- case FALSE: return(state); /* Receive failure, try again */
-
- default: return('A'); /* Anythig else, just "abort" */
- }
- }
-
-
- /*
- * s p a c k
- *
- * Send a Packet
- */
-
- static int spack(type,num,len,data)
- char type, *data;
- int num, len;
- {
- int i; /* Character loop counter */
- char chksum, buffer[100]; /* Checksum, packet buffer */
- register char *bufp; /* Buffer pointer */
-
- if (debug>1) /* Display outgoing packet */
- {
- if (data != NULL)
- data[len] = '\0'; /* Null-terminate data to print it */
- printf(" spack type: %c\n",type);
- printf(" num: %d\n",num);
- printf(" len: %d\n",len);
- if (data != NULL)
- printf(" data: \"%s\"\n",data);
- }
-
- bufp = buffer; /* Set up buffer pointer */
- for (i=1; i<=pad; i++) /* Issue any padding */
- fputc(padchar, cwfp);
-
- *bufp++ = SOH; /* Packet marker, ASCII 1 (SOH) */
- *bufp++ = tochar(len+3); /* Send the character count */
- chksum = tochar(len+3); /* Initialize the checksum */
- *bufp++ = tochar(num); /* Packet number */
- chksum += tochar(num); /* Update checksum */
- *bufp++ = type; /* Packet type */
- chksum += type; /* Update checksum */
-
- for (i=0; i<len; i++) /* Loop for all data characters */
- {
- *bufp++ = data[i]; /* Get a character */
- chksum += data[i]; /* Update checksum */
- }
- chksum = (((chksum&0300) >> 6)+chksum)&077; /* Compute final checksum */
- *bufp++ = tochar(chksum); /* Put it in the packet */
- *bufp = eol; /* Extra-packet line terminator */
- fwrite(buffer, 1, bufp - buffer + 1, cwfp); /* Send the packet */
- }
-
-
- /*
- * s p a r
- *
- * Fill the data array with my send-init parameters
- *
- */
-
- static void spar(data)
- char data[];
- {
- data[0] = tochar(MAXPACK); /* Biggest packet I can receive */
- data[1] = tochar(MYTIME); /* When I want to be timed out */
- data[2] = tochar(MYPAD); /* How much padding I need */
- data[3] = ctl(MYPCHAR); /* Padding character I want */
- data[4] = tochar(MYEOL); /* End-Of-Line character I want */
- data[5] = MYQUOTE; /* Control-Quote character I send */
- }
-
-
- #if MVUX
- /*
- * t a l k e r
- *
- * Special MV/UX sub-task to send keyboard characters to remote system.
- *
- */
-
- static void talker()
- {
- int cc; /* I/O buffer */
-
- while (1)
- {
- if ((cc = strip(fgetc(trfp))) == escchr)
- {
- if ((cc = strip(fgetc(trfp))) == escchr)
- fputc(escchr, lwfp);
- else
- {
- switch (cc)
- {
- case '#': /* simulate break */
- break;
-
- case 'c': /* return to local machine */
- case 'C':
- terminate = 1;
- break;
-
- case 'h': /* verify running status */
- case 'H':
- fprintf(twfp, "\r\n(Kermit)\r\n");
- fputs("\r\n\r\n", twfp);
- break;
-
- default:
- fputc(BELL, twfp);
- break;
- }
- }
- }
- else
- fputc(cc, lwfp);
- }
- }
- #endif
-
-
- /*
- * u s a g e
- *
- * Print summary of usage info and quit
- *
- */
-
- static void usage()
- {
- #if UCB4X | MVUX
- printf("Usage: kermit c[lbe line baud esc.char] (connect mode)\n");
- printf("or: kermit s[diflb line baud] file ... (send mode)\n");
- printf("or: kermit r[diflb line baud] (receive mode)\n");
- #else
- printf("Usage: kermit c[le line esc.char] (connect mode)\n");
- printf("or: kermit s[difl line] file ... (send mode)\n");
- printf("or: kermit r[difl line] (receive mode)\n");
- #endif
- exit(1);
- }
-